#include <windows.h>
#include <tchar.h>

#include "lib.h"
#include "npipe.h"

NamedPipe::NamedPipe(int32 MaxBufSize)
{
	bufSize = MaxBufSize;
	buf = new uint8[bufSize];
	if (!buf)
		bufSize = 0;

	overRd = new OVERLAPPED;
	if (overRd)
	{
		ZeroMemory(overRd, sizeof(OVERLAPPED));
		overRd->hEvent = (HANDLE)this;
	}

	overWr = new OVERLAPPED;
	if (overWr)
	{
		ZeroMemory(overWr, sizeof(OVERLAPPED));
		overWr->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	}

	connected = false;

	hPipe = INVALID_HANDLE_VALUE;
}

NamedPipe::~NamedPipe()
{
	close();

	delete overRd;

	if (overWr && overWr->hEvent)
		CloseHandle(overWr->hEvent);

	overRd = overWr = (LPOVERLAPPED)0;

	delete [] buf;
	buf = (uint8*)0;
	bufSize = 0;
}

bool8
NamedPipe::open(TCHAR* PipeName)
{
	DWORD mode;
	char buf[256];

	if (connected || !buf || !overRd || !overWr || !overWr->hEvent)
		return false;

	hPipe = CreateFile(PipeName,
		GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED, NULL);

	if (hPipe == INVALID_HANDLE_VALUE)
		return false;

	mode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
	if (!SetNamedPipeHandleState(hPipe, &mode, NULL, NULL))
	{
		CloseHandle(hPipe);
		hPipe = INVALID_HANDLE_VALUE;
		return false;
	}

	connected = true;
	
	return postRead();
}

void
NamedPipe::close()
{
	if (hPipe != INVALID_HANDLE_VALUE)
	{
		CloseHandle(hPipe);
		hPipe = INVALID_HANDLE_VALUE;
	}
	connected = false;
}

bool8
NamedPipe::send(uint8* Msg, int32 Len)
{
	if (connected)
	{
		DWORD amt;

		if (!WriteFile(hPipe, Msg, Len, &amt, overWr))
		{
			if (GetLastError() == ERROR_IO_PENDING &&
				!GetOverlappedResult(hPipe, overWr, &amt, TRUE))
			{
				close();
				return false;
			}
		}
		return true;
	}
	return false;
}

bool8
NamedPipe::postRead()
{
	if (connected)
	{
		if (!ReadFileEx(hPipe, buf, bufSize, overRd,
			(LPOVERLAPPED_COMPLETION_ROUTINE)ReadDone))
		{
			close();
			ioerror();
			return false;
		}
		return true;
	}
	return false;
}

void
NamedPipe::readDone(DWORD Err, DWORD Amt, LPOVERLAPPED Over)
{
	if (Err == 0 && Amt)
	{
		recvd(buf, (int32)Amt);
		if (!postRead())
		{
			close();
			ioerror();
		}
	}
	else
	{
		close();
		ioerror();
	}
}

VOID FAR PASCAL
NamedPipe::ReadDone(DWORD Err, DWORD Amt, LPOVERLAPPED Over)
{
	((NamedPipe*)Over->hEvent)->readDone(Err, Amt, Over);
}
